package top.buluoluo.apigateway.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class TokenManagerService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // Redis键前缀
    private static final String TOKEN_PREFIX = "auth:token:";
    private static final String USER_TOKENS_PREFIX = "user:tokens:";
    private static final String BLACKLIST_PREFIX = "blacklist:token:";

    /**
     * 存储Token与用户的关联关系
     */
    public void storeToken(String token, Long userId, long expirationTime) {
        String tokenKey = TOKEN_PREFIX + token;
        String userTokensKey = USER_TOKENS_PREFIX + userId;

        // 存储token -> userId的映射
        redisTemplate.opsForValue().set(
                tokenKey,
                userId.toString(),
                expirationTime,
                TimeUnit.MILLISECONDS
        );

        // 将token添加到用户的token集合中
        redisTemplate.opsForSet().add(userTokensKey, token);
        redisTemplate.expire(userTokensKey, expirationTime, TimeUnit.MILLISECONDS);

        log.info("用户 {} 的Token已存储，有效期: {}毫秒", userId, expirationTime);
    }

    /**
     * 验证Token是否有效（检查是否存在且不在黑名单中）
     */
    public boolean validateToken(String token) {
        // 1. 检查Token是否存在
        String tokenKey = TOKEN_PREFIX + token;
        boolean tokenExists = Boolean.TRUE.equals(redisTemplate.hasKey(tokenKey));

        // 2. 检查Token是否在黑名单中
        boolean isBlacklisted = isTokenBlacklisted(token);

        return tokenExists && !isBlacklisted;
    }

    /**
     * 获取Token对应的用户ID
     */
    public Long getUserIdByToken(String token) {
        String tokenKey = TOKEN_PREFIX + token;
        String userIdStr = (String) redisTemplate.opsForValue().get(tokenKey);
        return userIdStr != null ? Long.parseLong(userIdStr) : null;
    }

    /**
     * 用户主动退出登录 - 使Token失效并加入黑名单
     */
    public void logout(String token) {
        Long userId = getUserIdByToken(token);
        if (userId != null) {
            // 1. 计算Token剩余有效时间（用于黑名单过期时间）
            long remainingTime = getTokenRemainingTime(token);

            // 2. 将Token加入黑名单
            blacklistToken(token, remainingTime);

            // 3. 从用户Token集合中移除
            removeTokenFromUserSet(userId, token);

            // 4. 删除活跃Token记录（可选，黑名单已存在）
            // redisTemplate.delete(TOKEN_PREFIX + token);

            log.info("用户 {} 主动退出登录，Token已加入黑名单", userId);
        }
    }

    /**
     * 管理员强制下线 - 拉黑用户所有Token
     */
    public void forceLogoutUser(Long userId) {
        String userTokensKey = USER_TOKENS_PREFIX + userId;
        Set<Object> tokens = redisTemplate.opsForSet().members(userTokensKey);

        if (tokens != null && !tokens.isEmpty()) {
            for (Object tokenObj : tokens) {
                String token = (String) tokenObj;
                // 计算每个Token的剩余时间并加入黑名单
                long remainingTime = getTokenRemainingTime(token);
                blacklistToken(token, remainingTime);
            }

            // 清空用户Token集合
            redisTemplate.delete(userTokensKey);
            log.info("管理员已强制用户 {} 下线，拉黑 {} 个Token", userId, tokens.size());
        }
    }

    /**
     * 拉黑特定Token（强制单个设备下线）
     */
    public void blacklistToken(String token, long remainingTime) {
        if (remainingTime <= 0) {
            log.info("Token已过期，无需加入黑名单");
            return;
        }

        String blacklistKey = BLACKLIST_PREFIX + token;

        // 将Token加入黑名单，使用剩余有效时间作为黑名单有效期
        redisTemplate.opsForValue().set(
                blacklistKey,
                "blacklisted",
                remainingTime,
                TimeUnit.MILLISECONDS
        );

        log.info("Token已被拉黑，黑名单有效期: {}毫秒", remainingTime);
    }

    /**
     * 检查Token是否在黑名单中
     */
    public boolean isTokenBlacklisted(String token) {
        String blacklistKey = BLACKLIST_PREFIX + token;
        Boolean exists = redisTemplate.hasKey(blacklistKey);
        log.debug("Redis中该token是否被拉黑查询结果: {}", exists);
        return Boolean.TRUE.equals(redisTemplate.hasKey(blacklistKey));
    }

    /**
     * 移除黑名单中的Token（解除拉黑）
     */
    public void removeFromBlacklist(String token) {
        String blacklistKey = BLACKLIST_PREFIX + token;
        redisTemplate.delete(blacklistKey);
        log.info("Token已从黑名单中移除");
    }

    /**
     * 获取用户的所有活跃Token（管理员功能）
     */
    public Set<Object> getUserActiveTokens(Long userId) {
        String userTokensKey = USER_TOKENS_PREFIX + userId;
        return redisTemplate.opsForSet().members(userTokensKey);
    }

    /**
     * 获取Token剩余有效时间（毫秒）
     */
    private long getTokenRemainingTime(String token) {
        String tokenKey = TOKEN_PREFIX + token;
        Long expireTime = redisTemplate.getExpire(tokenKey, TimeUnit.MILLISECONDS);
        return expireTime != null ? expireTime : 0;
    }

    /**
     * 从用户Token集合中移除特定Token
     */
    private void removeTokenFromUserSet(Long userId, String token) {
        String userTokensKey = USER_TOKENS_PREFIX + userId;
        redisTemplate.opsForSet().remove(userTokensKey, token);

        // 如果集合为空，删除整个键
        Long size = redisTemplate.opsForSet().size(userTokensKey);
        if (size != null && size == 0) {
            redisTemplate.delete(userTokensKey);
        }
    }
}